/**
 * 菜单类 无极菜单可以无限制连级
 * elem dom节点
 * data 数据源
 * singleClick单击事件
 * hoverEvent hover事件
 * contextmenu 右键菜单（后续加入）
 * leave 鼠标画出事件
 * gaps 为间距
 * direction 为子菜单定位的方向，left,right,top,bottom，默认为left
 * highLightColor 菜单高亮颜色
 * 实例地址 https://gitee.com/SimonaGo/infinite-menu
 */
 class Menu {
    constructor(params) {
        this.params = new Proxy(params, {
            get: function (target, propKey) {
                if (!Reflect.has(target, propKey)) {
                    Reflect.set(target, propKey, false); //更改参数的默认值为false
                    return Reflect.get(target, propKey);
                } else {
                    return Reflect.get(target, propKey);
                }
            },
        });
        this.checkParams();
        this.pNodes = new Set([]);
        this.childNodes = new Set([]);
        this.uuid = null;
        this.resData = []; // 结构化数据
        this.currentData = {};
        this.curActive = new Set([]); //记录高亮按钮
        this.rootId = null; // 根节点ID
        this.showStyle = this.params.showStyle; // 菜单闭合方式：auto同时只存在一个菜单，manual菜单闭合由传入决定，mixin菜单组合式展开闭合
        this.treeNodes = document.createDocumentFragment(); //虚拟dom,提升页面绘制效率
    }
    checkParams() {
        if (!this.params.elem) {
            throw new Error("Expected a Dom node");
        }
        if (!this.params.data) {
            throw new Error("Expected a data source");
        }
    }
    /**
     * 保证节点ID唯一
     * @returns 生成唯一ID,GUID的总数为2^128（3.4×10^38）个
     */
    uuidMade() {
        let uuid = this.S4() + this.S4() + "-" + this.S4() + "-" + this.S4() + "-" + this.S4() + "-" + this.S4() + this.S4() + this.S4();
        return uuid;
    }
    /**
     * 随机数
     * @returns
     */
    S4() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    }
    /**
     * 构造父节点
     * @param {*} param
     * @param {*} uuid 节点唯一ID
     */
    parentNode(param, uuid) {
        const parentFragment = {
            el: "li",
            attributes: {
                id: uuid,
                title: param.name,
                class: "root",
            },
            children: [
                {
                    el: "i",
                    attributes: {
                        class: `iconfont ${param.icon}`,
                    },
                },
                {
                    el: "text",
                    nodeValue: `${param.name}`,
                },
                {
                    el: "ul",
                    attributes: {
                        class: "wrapper-menulist-root",
                    },
                },
            ],
        };
        const menuHtml = this.createElement(parentFragment);
        this.treeNodes.appendChild(menuHtml);
        this.callback(menuHtml, uuid, param);
        if (param.children) {
            this.childrenNode(param.children, menuHtml, uuid);
        }
    }
    /**
     * 构造子节点
     * @param {*} node
     * @param {*} uuid 节点唯一ID
     */
    childrenNode(node, menuHtml, uuid) {
        for (let item of node) {
            let cid = this.uuidMade();
            const childFragment = {
                el: "li",
                attributes: {
                    id: cid,
                    title: item.name,
                },
                children: [
                    {
                        el: "i",
                        attributes: {
                            class: `iconfont ${item.icon}`,
                        },
                    },
                    {
                        el: "text",
                        nodeValue: `${item.name}`,
                    },
                    {
                        el: "ul",
                        attributes: {
                            class: "wrapper-menulist-root",
                        },
                    },
                ],
            };
            const childHtml = this.createElement(childFragment);
            menuHtml.getElementsByTagName("ul")[0].appendChild(childHtml);
            this.callback(childHtml, cid, item);
            if (item.children && item.children.length) {
                this.childrenNode(item.children, cid);
            }
        }
    }
    /**
     *
     * @param {*} object
     * @returns 虚拟节点
     */
    createElement(object) {
        let element = this.createType(object);
        this.setAttribute(element, object);
        if (Array.isArray(object.children)) {
            for (let item of object.children) {
                let children = this.createType(item);
                this.setAttribute(children, item);
                element.appendChild(children);
            }
        }
        return element;
    }
    setAttribute(element, object) {
        if (object.attributes) {
            const keys = Reflect.ownKeys(object.attributes);
            if (keys) {
                for (let item of keys) {
                    element.setAttribute(item, Reflect.get(object.attributes, item));
                }
            }
        }
        if (object.nodeValue) {
            element.innerHTML = object.nodeValue;
        }
    }
    createType(object) {
        let element = null;
        if (object.type && object.type == "text") {
            element = document.createTextNode(object.node);
        } else {
            element = document.createElement(object.el);
        }
        return element;
    }
    /**
     * 结构化数据
     */
    StructuralData(originData) {
        for (let i = 0; i < originData.length; i++) {
            let id = originData[i].id,
                children = null;
            if (id) {
                children = originData.filter(res => {
                    return res.parentId == id;
                });
            }
            if (!originData[i].parentId) {
                this.resData.push(originData[i]);
            } else if (children.length) {
                this.resData.push(originData[i]);
            }
            if (children && children.length) {
                originData[i].children = children;
                this.StructuralData(children);
            }
        }
    }
    /**
     * 回调
     * @param {*} param
     * @param {*} uuid
     */
    callback(element, uuid, curData) {
        const that = this;
        if (this.params.click) {
            element.addEventListener(
                "click",
                {
                    handleEvent: function (event) {
                        event.stopImmediatePropagation();
                        that.currentData = that.params.data.find(res => res.id === curData.id);
                        let direction = null;
                        if (that.params.direction == "top") {
                            const parents = $(`#${uuid}`).parents()[1];
                            const params = $(parents).attr("id");
                            if (params && params.search("-root") != -1) {
                                direction = "left";
                            }
                        }
                        if (uuid.search("-root") != -1) {
                            that.rootId = uuid;
                        }
                        that.uuid = uuid;
                        that.toggleShow(uuid, direction);
                        that.params.click(that.currentData, uuid);
                    },
                }
            );
        }
        if (this.params.hover) {
            $(`#${uuid}`).on("mouseenter", function (event) {
                event.stopPropagation();
                event.stopImmediatePropagation();
                that.currentData = that.params.data.find(res => res.id === curData.id);
                that.params.hover(that.currentData, $(`#${uuid}`));
            });
        }
        if (this.params.leave) {
            $(`#${uuid}`).on("mouseout", function (event) {
                event.stopPropagation();
                event.stopImmediatePropagation();
                that.currentData = that.params.data.find(res => res.id === curData.id);
                that.params.leave(that.currentData, $(`#${uuid}`));
            });
        }
    }
    /**
     * 初始化样式
     * @param {*} uuid
     */
    initStyle(uuid) {
        $(`#${uuid}  .wrapper-menulist-root`).css({
            display: "none",
            opacity: 0,
            width: "max-content",
            overflow: "hidden",
        });
    }
    /**
     * 切换动画
     * @param {*} uuid
     */
    toggleShow(uuid, way) {
        const width = $(`#${uuid}> .wrapper-menulist-root`).width();
        const left = $(`#${uuid}`).width(),
            gaps = this.params.gaps || 10,
            direction = way || this.params.direction || "left";

        $(`#${uuid} > .wrapper-menulist-root`).css({
            [direction]: left + gaps,
            width: 0,
            overflow: "hidden",
        });
        $(`#${uuid} > .wrapper-menulist-root`).toggle();
        $(`#${uuid} > .wrapper-menulist-root`).animate(
            {
                opacity: 1,
                width: width,
                [direction]: left + gaps,
            },
            function () {
                $(this).css({ overflow: "visible", width: "max-content" });
            }
        );
        if ($(`#${uuid} > .wrapper-menulist-root`).css("display") === "none") {
            this.initStyle(uuid);
        }
        this.showType(uuid);
    }
    /**
     * 切换方式
     * @param {*} uuid
     */
    showType(uuid) {
        const style = this.showStyle;
        let showStyle = false,
            auto = [],
            manual = [];
        if (style && style.auto) {
            showStyle = "auto";
        } else if (style && style.manual) {
            showStyle = "manual";
        } else if (style && style.mixin) {
            auto = style.mixin.auto;
            manual = style.mixin.manual;
            showStyle = "mixin";
        } else {
            showStyle = "auto";
        }
        switch (showStyle) {
            case "auto":
                this.autoClose(uuid);
                break;
            case "mixin":
                break;
        }
    }
    /**
     * 单节点展示闭合其他兄弟节点
     * @param {*} uuid
     */
    autoClose(uuid) {
        let siblings = $(`#${uuid}`).siblings();
        const sibSet = new Set([]);
        $(`#${uuid}`).siblings().find(".wrapper-menulist-root").css({
            display: "none",
            opacity: 0,
            width: "max-content",
            overflow: "hidden",
        }); // 隐藏同级兄弟节点的子节点
        siblings.each((i, e) => {
            sibSet.add($(e).attr("id"));
        }); // find不需要高亮的ID
        for (let item of sibSet) {
            this.highlightCancle(item);
        } // 清除curActive中不需要高亮的ID并移除color
        if (this.curActive.has(uuid)) {
            this.curActive.delete(uuid);
            $(`#${uuid} > text, #${uuid} > i `).css({
                color: "unset",
            });
        } else {
            this.curActive.add(uuid);
        } // 检查当前Id是否已经高亮
        this.changeColor();
    }
    /**
     * 切换高亮颜色
     * @param {*} uuid
     */
    changeColor() {
        let color = this.params.highLightColor || "rgba(0, 204, 255, 0.98823529)";
        for (let item of this.curActive) {
            $(`#${item} > text, #${item} > i `).css({
                color: color,
            });
        }
    }
    /**
     * 取消高亮菜单按钮
     * @param {*} uuid
     */
    highlightCancle(uuid) {
        if (this.curActive.has(uuid)) {
            this.curActive.delete(uuid);
            $(`#${uuid} > text, #${uuid} > i `).css({
                color: "unset",
            });
        }
    }
}

/**
 * 垂直菜单
 */
class VerticalLine extends Menu {
    constructor(...args) {
        super(...args);
        this.documentEvent();
    }
    /**
     * 初始化菜单方法
     */
    initVerticalMenus() {
        const elem = this.params.elem;
        const data = this.params.data;
        const virtualDom = this.treeNodes;
        const cloneData = JSON.parse(JSON.stringify(data));
        super.StructuralData(cloneData);
        if (this.resData) {
            for (let item of this.resData) {
                let uuid = super.uuidMade() + "-root";
                super.parentNode(item, uuid);
                $("#" + `${elem}`).append(virtualDom);
            }
        }
    }
    /**
     * 文档点击事件
     */
    documentEvent() {
        const that = this;
        $(document).on("click", function (e) {
            $(`.wrapper-menulist-root`).css({
                display: "none",
                opacity: 0,
                width: "max-content",
                overflow: "hidden",
            });
            var uuid = that.rootId;
            that.highlightCancle(uuid);
        });
    }
}
